package com.ccteam.fluidmusic.fluidmusic.media.scanner

import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.MediaStore
import android.provider.MediaStore.Audio.Media
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaMetadataCompat
import cn.hutool.extra.pinyin.PinyinUtil
import com.ccteam.fluidmusic.fluidmusic.common.utils.MediaIDHelper
import com.ccteam.fluidmusic.fluidmusic.media.extensions.*
import dagger.hilt.android.qualifiers.ApplicationContext
import java.util.concurrent.TimeUnit
import javax.inject.Inject

/**
 * @author Xiaoc
 * @since 2020.12.25
 *
 * 基于MediaStore的本地扫描器
 * 使用 [android.provider.MediaStore] 媒体库，扫描其中的内容
 * 扫描媒体库中的所有歌曲内容
 */
class MediaStoreScanner @Inject constructor(
    private val mediaIDHelper: MediaIDHelper,
    @ApplicationContext private val context: Context){

    companion object {
        /**
         * 兼容性字符串，因为安卓Q以下MediaStore不带
         */
        const val DURATION: String = "duration"
    }

    private val mediaProjection: Array<String> = arrayOf(
            Media._ID,
            Media.TITLE,
            Media.ALBUM_ID,
            Media.ALBUM,
            DURATION,
            Media.TRACK,
            Media.ARTIST_ID,
            Media.ARTIST,
            Media.YEAR
    )

    private val albumProjection: Array<String> = arrayOf(
        MediaStore.Audio.Albums.ALBUM_ID,
        MediaStore.Audio.Albums.ALBUM,
        MediaStore.Audio.Albums.ARTIST,
        MediaStore.Audio.Albums.FIRST_YEAR,
        MediaStore.Audio.Albums.NUMBER_OF_SONGS
    )

    private val artistProjection = arrayOf(
        MediaStore.Audio.Artists._ID,
        MediaStore.Audio.Artists.ARTIST,
        MediaStore.Audio.Artists.NUMBER_OF_TRACKS,
        MediaStore.Audio.Artists.NUMBER_OF_ALBUMS
    )

    private val mediaSelection = "${Media.IS_MUSIC} != 0 AND $DURATION > ?"

    /**
     * 检索规则中排除小于5秒的歌曲
     */
    private val mediaSelectionArgs = arrayOf(
        TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS).toString())

    /**
     * 从 MediaStore 媒体库中扫描所有音乐
     */
    fun scanAllFromMediaStore(): List<MediaMetadataCompat> {
        val cursor = context.contentResolver.query(
                Media.EXTERNAL_CONTENT_URI,
                mediaProjection,
                mediaSelection,
                mediaSelectionArgs,
                Media.DEFAULT_SORT_ORDER
        )

        return handleMediaMusicCursor(cursor)
    }

    private fun getAlbumUri(albumId: String): String{
        val artworkUri = Uri.parse(MediaIDHelper.ALBUM_ART_URI)
        return Uri.withAppendedPath(artworkUri,albumId).toString()
    }

    /**
     * 从 MediaStore 媒体库中扫描专辑
     */
    fun scanAlbumFromMediaStore(): List<MediaMetadataCompat>{
        val cursor = context.contentResolver.query(
            MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
            albumProjection,
            null,
            null,
            MediaStore.Audio.Albums.DEFAULT_SORT_ORDER
        )
        return handleAlbumCursor(cursor)
    }

    /**
     * 从 MediaStore 媒体库中专辑的音乐
     * @param albumId 专辑ID
     */
    fun scanAlbumMusic(albumId: String): List<MediaMetadataCompat>{
        val cursor = context.contentResolver.query(
            Media.EXTERNAL_CONTENT_URI,
            mediaProjection,
            "${Media.IS_MUSIC} != 0 AND $DURATION > ? AND ${Media.ALBUM_ID} = ?",
            arrayOf(
                TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS).toString(),albumId),
            "${Media.TRACK} ASC" /* 专辑歌曲默认按照曲目顺序排序 */
        )
        return handleMediaMusicCursor(cursor,false)
    }

    /**
     * 从 MediaStore 媒体库中歌手的音乐
     * @param artistId 歌手ID
     */
    fun scanArtistMusic(artistId: String): List<MediaMetadataCompat>{
        val cursor = context.contentResolver.query(
            Media.EXTERNAL_CONTENT_URI,
            mediaProjection,
            "${Media.IS_MUSIC} != 0 AND $DURATION > ? AND ${Media.ARTIST_ID} = ?",
            arrayOf(
                TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS).toString(),artistId),
            Media.DEFAULT_SORT_ORDER /* 专辑歌曲默认按照曲目顺序排序 */
        )
        return handleMediaMusicCursor(cursor)
    }

    /**
     * 从 MediaStore 媒体库中歌手的专辑
     * @param artistId 歌手ID
     */
    fun scanArtistAlbum(artistId: String): List<MediaMetadataCompat>{
        val cursor = context.contentResolver.query(
            MediaStore.Audio.Artists.Albums.getContentUri("external",artistId.toLong()),
            albumProjection,
            null,
            null,
            null
        )
        return handleAlbumCursor(cursor)
    }

    fun scanArtistFromMediaStore(): List<MediaMetadataCompat>{
        val artistList = mutableListOf<MediaMetadataCompat>()
        val cursor = context.contentResolver.query(
            MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
            artistProjection,
            null,
            null,
            MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
        )
        cursor?.use {
            val artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST)
            val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID)
            val albumNumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS)
            val trackColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS)

            while (it.moveToNext()){
                val id = it.getLong(idColumn)
                val artistName = it.getString(artistColumn)
                val albumNum = it.getLong(albumNumColumn)
                val trackNum = it.getLong(trackColumn)

                // 获得当前歌手的头像（取该歌手第一张专辑封面为头像）
                val albumId = getArtistAlbumArtByAlbumId(MediaStore.Audio.Artists.Albums.getContentUri("external",id))
                val artistAlbumArtUri = if(albumId != null){
                    getAlbumUri(albumId.toString())
                } else {
                    null
                }

                val metadataBuilder = MediaMetadataCompat.Builder().apply {
                    /* __FM_LOCAL_ARTIST__/artistId */
                    this.id = mediaIDHelper.createMediaId(MediaIDHelper.LOCAL_MEDIA_ARTIST,id.toString())
                    this.title = artistName
                    this.displayTitle = artistName
                    this.displaySubtitle = albumNum.toString()
                    this.displayDescription = trackNum.toString()
                    this.displayIconUri = artistAlbumArtUri
                    this.albumArtUri = artistAlbumArtUri
                    this.artist = artistName
                    this.trackNumber = trackNum
                    this.artistId = id.toString()
                    this.flag = MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
                }

                artistList.add(metadataBuilder.build())

            }
        }
        // 进行拼音排序
        artistList.sortBy {
            PinyinUtil.getPinyin(it.title)
        }
        return artistList
    }

    private fun getArtistAlbumArtByAlbumId(albumsUri: Uri): Long?{
        val cursor = context.contentResolver.query(albumsUri,
            arrayOf(MediaStore.Audio.Artists.Albums.ALBUM_ID),null,null,null)
        cursor?.use {
            return if(it.moveToNext()){
                it.getLong(it.getColumnIndexOrThrow(MediaStore.Audio.Artists.Albums.ALBUM_ID))
            } else {
                null
            }
        }
        return null
    }

    private fun handleAlbumCursor(cursor: Cursor?): List<MediaMetadataCompat>{
        val albumList = mutableListOf<MediaMetadataCompat>()

        cursor?.use {
            val albumIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM_ID)
            val albumTitleColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM)
            val artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ARTIST)
            val trackNumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS)
            val yearColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.FIRST_YEAR)

            while (it.moveToNext()){
                val albumId = it.getLong(albumIdColumn)
                val albumTitle = it.getString(albumTitleColumn)
                val artist = it.getString(artistColumn)
                val trackNum = it.getLong(trackNumColumn)
                val year = it.getLong(yearColumn)

                val metadataBuilder = MediaMetadataCompat.Builder().apply {
                    /* __FM_LOCAL_ALBUM__/albumId */
                    this.id = mediaIDHelper.createMediaId(MediaIDHelper.LOCAL_MEDIA_ALBUM,albumId.toString())
                    this.title = albumTitle
                    this.displayTitle = albumTitle
                    this.displaySubtitle = artist
                    this.displayDescription = trackNum.toString()
                    this.album = albumTitle
                    this.artist = artist
                    this.trackCount = trackNum
                    this.albumId = albumId.toString()
                    this.year = year
                    this.flag = MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
                }

                val albumUri = getAlbumUri(albumId.toString())
                metadataBuilder.albumArtUri = albumUri
                metadataBuilder.displayIconUri = albumUri

                albumList.add(metadataBuilder.build())
            }

        }
        // 进行拼音排序
        albumList.sortBy {
            PinyinUtil.getPinyin(it.title)
        }
        return albumList
    }

    private fun handleMediaMusicCursor(cursor: Cursor?,handlePinyin: Boolean = true): List<MediaMetadataCompat>{
        val localMusicList = mutableListOf<MediaMetadataCompat>()
        cursor?.use {
            val colId = it.getColumnIndexOrThrow(Media._ID)
            // 歌曲标题
            val colTitle = it.getColumnIndexOrThrow(Media.TITLE)
            val colAlbum = it.getColumnIndexOrThrow(Media.ALBUM)
            val colAlbumId = it.getColumnIndexOrThrow(Media.ALBUM_ID)
            val colArtist = it.getColumnIndexOrThrow(Media.ARTIST)
            val colArtistId = it.getColumnIndexOrThrow(Media.ARTIST_ID)
            val colDuration = it.getColumnIndexOrThrow(DURATION)
            val colTrack = it.getColumnIndexOrThrow(Media.TRACK)

            while (it.moveToNext()) {
                val id = it.getLong(colId)
                val title = it.getString(colTitle)
                val album = it.getString(colAlbum)
                val artist = it.getString(colArtist)
                val duration = it.getLong(colDuration)
                val track = it.getInt(colTrack)
                val albumId = it.getLong(colAlbumId)
                val artistId = it.getLong(colArtistId)

                val musicUri: Uri = ContentUris.withAppendedId(Media.EXTERNAL_CONTENT_URI, id)

                val metadataBuilder = MediaMetadataCompat.Builder().apply {
                    this.id = id.toString()
                    this.title = title
                    this.displayTitle = title
                    this.displaySubtitle = artist
                    this.displayDescription = album
                    this.album = album
                    this.artist = artist
                    this.duration = duration
                    this.trackNumber = track.toLong()
                    this.mediaUri = musicUri.toString()
                    this.albumId = albumId.toString()
                    this.artistId = artistId.toString()
                    this.flag = MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
                    /* 离线资源标志 */ this.isOffline = 1L
                }

                val albumUri = getAlbumUri(albumId.toString())
                metadataBuilder.albumArtUri = albumUri
                metadataBuilder.displayIconUri = albumUri

                localMusicList.add(metadataBuilder.build())
            }
        }
        if(handlePinyin){
            // 进行拼音排序
            localMusicList.sortBy {
                PinyinUtil.getPinyin(it.title)
            }
        }
        return localMusicList
    }


}